#pragma once

#include "log.hpp"
#include <iostream>
#include <cstdint>
#include <sys/types.h>    
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

extern Log lg;

enum
{
    SOCKET_ERR = 1,
    BIND_ERR,
    LISTEN_ERR
};

class TcpServer
{
    static const int defaulebacklog = 10;
public:
    TcpServer(int port = 8888):port_(port){}

    void Service(int fd,const struct sockaddr_in &peer)
    {
        while(true)
        {
            char buf[1024] = {0};
            int n =recv(fd,buf,1023,0);
            if(n < 0){
                lg(Warning,"server recv failed");
                break;
            }else if(n == 0){
                lg(Info,"client quit");
                break;
            }
            buf[n] = 0;
            lg(Info,"server recv a mag:%s",buf);

            std::string echo_string = "server echo@";
            echo_string += buf;

            n = send(fd,echo_string.c_str(),echo_string.size(),0);
            if(n < 0){
                lg(Warning,"server send failed");
                break;
            }
        }

        close(fd);
    }

public:
    void Init()
    {
        //创建套接字
        listen_fd_ = socket(AF_INET,SOCK_STREAM,0);
        if(listen_fd_ < 0){
            lg(Fatal,"create socket failed");
            exit(SOCKET_ERR);
        }

        //开启地址复用
        int val = 1;
        setsockopt(listen_fd_,SOL_SOCKET,SO_REUSEADDR | SO_REUSEPORT,&val,sizeof(val));

        //bind
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        local.sin_addr.s_addr = INADDR_ANY;
        socklen_t len = sizeof(local);
        int ret = bind(listen_fd_,(struct sockaddr*)&local,len);
        if(ret < 0){
            lg(Fatal,"server bind failed, err string:%s",strerror(errno));
            exit(BIND_ERR);
        }

        //监听
        ret = listen(listen_fd_,defaulebacklog);
        if(ret < 0){
            lg(Fatal,"server listen failed");
            exit(LISTEN_ERR);
        }

    }

    void Run()
    {
        while(true){
            //获取新连接
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int newfd = accept(listen_fd_,(struct sockaddr*)&peer,&len);
            if(newfd < 0){
                lg(Warning,"server accept failed");
                continue;
            }
            //获取对端IP和端口号
            std::string clientip = inet_ntoa(peer.sin_addr);
            uint16_t clientport = ntohs(peer.sin_port);
            lg(Info,"server get a new link, ip:%s, port:%d",clientip.c_str(),clientport);

            //完成服务
            Service(newfd,peer);
        }
    };

private:
    int listen_fd_;
    uint16_t port_;
};